#include "stdafx.h"
#include "editor.h"
#include "framebuffer.h"

#include <QtGui/QApplication>
#include <vector>
#include "simd.h"

//#include "engine/d3d.h"
/*
#include "engine/DirectXDependencies.h"


#include "engine/geom.h"
#include "engine/asset.h"
#include "engine/data.h"
#include "engine/gbuffer.h"
#include "engine/light.h"
#include "enginewidget.h"
#include "inputstate.h"
#include "engine/model.h"*/

#include "engine/glm/glm.hpp"
#include "engine/glm/ext.hpp"
using namespace glm;


mat4 view_matrix(vec3 eye, vec3 focus, vec3 up)
{
	return lookAt(eye, focus, up);
}

mat4 proj_matrix(float w, float h)
{
	float ar = w/h;
	return perspective(45.0f, ar, 1.0f, 1000.0f);
}
struct Ray
{
	vec3 start;
	vec3 direction;
	vec3 at(float t)
	{
		return start + t * direction;
	}
};

__inline __m256 multiply(__m256 a, __m256 b)
{
	return _mm256_mul_ps(a, b);
}
__inline __m256 add(__m256 a, __m256 b)
{
	return _mm256_add_ps(a, b);
}
__inline __m256 subtract(__m256 a, __m256 b)
{
	return _mm256_sub_ps(a, b);
}
__inline __m256 divide(__m256 a, __m256 b)
{
	return _mm256_div_ps(a, b);
}

const int RAYS_PER_SIMD_ITERATION = 8;
const int ITERATIONS_PER_BATCH = 64;
const int RAYS_PER_BATCH = ITERATIONS_PER_BATCH * RAYS_PER_SIMD_ITERATION;


struct VecSOA
{
	__m256 x;
	__m256 y;
	__m256 z;
	__inline void normalize()
	{
		static auto one_256 = as_mm256(1);
		//auto len = _mm256_sqrt_ps(add(add(multiply(x, x), multiply(y, y)), multiply(z, z)));
		__m256 invlen = _mm256_rsqrt_ps(add(add(multiply(x, x), multiply(y, y)), multiply(z, z)));//divide(one_256, len);
		x = multiply(x, invlen);
		y = multiply(y, invlen);
		z = multiply(z, invlen);
	}
};
struct RaySOA
{
	v3stream origin;
	v3stream direction;
};

struct Frustrum
{
	Ray top_left;
	Ray top_right;
	Ray bottom_left;
	Ray bottom_right;
	vec3 origin;
};
vec3 screen_to_world(vec2 p, float z, vec2 viewport, mat4 inv_viewproj)
{
	vec2 np = ((p / viewport) - vec2(0.5)) * vec2(2, -2);
	vec4 wp = inv_viewproj * vec4(np, z, 1);
	return vec3(wp) / wp.w;
}
Frustrum inv_viewproj_to_frustrum(mat4 inv_viewproj, vec2 viewport)
{
	Frustrum frustrum;

	frustrum.top_left.start = screen_to_world(vec2(0, 0), -1, viewport, inv_viewproj);
	frustrum.top_right.start = screen_to_world(vec2(viewport.x, 0), -1, viewport, inv_viewproj);
	frustrum.bottom_left.start = screen_to_world(vec2(0, viewport.y), -1, viewport, inv_viewproj);
	frustrum.bottom_right.start = screen_to_world(vec2(viewport.x, viewport.y), -1, viewport, inv_viewproj);

	auto end = screen_to_world(vec2(0, 0), 1, viewport, inv_viewproj);

	frustrum.top_left.direction = normalize(screen_to_world(vec2(0, 0), 1, viewport, inv_viewproj) - frustrum.top_left.start);
	frustrum.top_right.direction = normalize(screen_to_world(vec2(viewport.x, 0), 1, viewport, inv_viewproj) - frustrum.top_right.start);
	frustrum.bottom_left.direction = normalize(screen_to_world(vec2(0, viewport.y), 1, viewport, inv_viewproj) - frustrum.bottom_left.start);
	frustrum.bottom_right.direction = normalize(screen_to_world(vec2(viewport.x, viewport.y), 1, viewport, inv_viewproj) - frustrum.bottom_right.start);


	return frustrum;
}
Ray screen_to_world_ray_by_interpolation(vec2 p, vec2 viewport, vec3 origin, Frustrum frustrum)
{
	Ray ray;

	vec2 ratio = (p / viewport);
	vec3 d0 = normalize(ratio.x * frustrum.top_right.direction + (1 - ratio.x) * frustrum.top_left.direction);
	vec3 d1 = normalize(ratio.x * frustrum.bottom_right.direction + (1 - ratio.x) * frustrum.bottom_left.direction);
	vec3 d = d0 * (1 - ratio.y) + d1 * ratio.y;
	ray.direction = normalize(d);

	vec3 right = (frustrum.bottom_right.start - frustrum.top_right.start) * ratio.y + frustrum.top_right.start;
	vec3 left = (frustrum.bottom_left.start - frustrum.top_left.start) * ratio.y + frustrum.top_left.start;

	ray.start = (right - left) * ratio.x + left;
	return ray;
	
}

//returns offsets @ the near plane in world space
vec3 world_offsets_per_pixel(float fov, vec2 viewport, float aspect_ratio, float z_near)
{	
	vec3 offset;
	float k = tan(radians(fov) / 2);
	offset.x = aspect_ratio * 2 * z_near * k / viewport.x;
	offset.y = -2 * z_near * k / viewport.y;
	offset.z = 0; //for convenience
	return offset;
}

struct RaySphereIntersection
{
	vec3 p[2];
	float t[2];
	int count;
};
struct RaySphereIntersectionSOA
{
	__m256 p[2];
	__m256 t[2];
	int count[8];
};
__inline void subtract(VecSOA* a, VecSOA* b, VecSOA* result)
{
	result->x = _mm256_sub_ps(a->x, b->x);
	result->y = _mm256_sub_ps(a->y, b->y);
	result->z = _mm256_sub_ps(a->z, b->z);
}
__inline __m256 dot(VecSOA& a, VecSOA& b)
{
	__m256 x = _mm256_mul_ps(a.x, b.x);
	__m256 y = _mm256_mul_ps(a.y, b.y);
	__m256 z = _mm256_mul_ps(a.z, b.z);
	return _mm256_add_ps(_mm256_add_ps(x, y), z);
}



#define SIMD_CONSTANTS(a, b, c, d) {a, b, c, d, a, b, c, d}

#define SIMD_CONSTANT_0(d) _mm256_shuffle_ps(d, d, 0)
#define SIMD_CONSTANT_1(d) _mm256_shuffle_ps(d, d, 0x55)
#define SIMD_CONSTANT_2(d) _mm256_shuffle_ps(d, d, 0xaa)
#define SIMD_CONSTANT_3(d) _mm256_shuffle_ps(d, d, 0xff)

auto two_256 = f32stream(2);
auto four_256 = f32stream(4);
auto neg_one_256 = f32stream(-1);

void ray_sphere_intersection(
	v3stream sphere_origin, 
	f32stream sphere_radius, 
	RaySOA* __restrict  rays, 
	RaySphereIntersectionSOA* __restrict  intersection,
	int count)
{
	//static __m256 constants = SIMD_CONSTANTS(-1, 1, 2, 4);
	for(int i = 0; i < count; i++)
	{
		auto __restrict cur_ray = rays + i;
		auto __restrict cur_intersection = intersection + i; 

		//VecSOA dp;
		auto dp = cur_ray->origin - sphere_origin;
		//subtract(&rays.origin, &sphere_origin, &dp);
		auto r = dot(dp, cur_ray->direction);
		//f32stream a = one_256;
		f32stream b = two_256 * r;
		f32stream c = dot(dp, dp) - sphere_radius * sphere_radius;

		f32stream d_squared = b * b - four_256 * /*a */ c; // a = 1

		f32stream d = sqrt(d_squared);

		f32stream inv_two_a = inverse(two_256); // a = 1

		f32stream t0 = neg_one_256 * (b - d) * inv_two_a;
		f32stream t1 = neg_one_256 * (b + d) * inv_two_a;


		//just for testing
		cur_intersection->t[0] = t0.data;

		for(int j = 0; j < 8; j++) cur_intersection->count[j] = 1;
		
	}
	
}
#define align32 __declspec(align(32)) 
void light(QRgb* fb, RaySphereIntersectionSOA ray_buffer[800], int start, int valid_rays_count)
{
	for(int i = 0; i < valid_rays_count; i+=RAYS_PER_SIMD_ITERATION)
	{
		align32 float ray_t[RAYS_PER_SIMD_ITERATION];

		_mm256_store_ps(ray_t, ray_buffer[i / RAYS_PER_SIMD_ITERATION].t[0]);
		
		for(int j = 0; (j < RAYS_PER_SIMD_ITERATION) && (i + j < valid_rays_count); j++)
		{
			float t = ray_t[j];
			fb[start + i + j] = qRgb(t * 255 , 0, 0);

			
		}
	}
}
struct RayBuffer
{
	struct 
	{
		f32stream x[2000];
		f32stream y[2000];
		f32stream z[2000];
	} position;
	struct 
	{
		f32stream x[2000];
		f32stream y[2000];
		f32stream z[2000];
	} direction;

	void normalize_directions()
	{		
		normalize(direction.x, direction.y, direction.z, 2000);
	}
};
namespace eyeray
{
	const f32stream zero(0.0f);
	const f32stream eight(8.0f);
	const float increasing_data[] = {0, 1, 2, 3, 4, 5, 6, 7};
	const f32stream increasing(increasing_data);

	struct GenerateEyeRaysInfo
	{
		//in
		int y0;
		int y1;
		int width;
		int buffer_offset;

		vec3 dp_per_pixel;
		vec3 eye_position;
		vec3 frustrum_top_left;

		//out
		RayBuffer* __restrict ray_buffer;
	};
	void generate_eye_rays_serial(const GenerateEyeRaysInfo& info)
	{
		int y0 = info.y0;
		int y1 = info.y1;
		int width = info.width;
		int blocks = ceil(width / 8.0);
		
		float base_x = info.frustrum_top_left.x;
		float base_y = info.frustrum_top_left.y;
		float base_z = info.frustrum_top_left.z;

		float x_inc = info.dp_per_pixel.x;
		float y_inc = info.dp_per_pixel.y;
		float z_inc = info.dp_per_pixel.z;

		float eye_x = info.eye_position.x;
		float eye_y = info.eye_position.y;
		float eye_z = info.eye_position.z;

		float cur_ws_z = base_z;
		float cur_ws_y = base_y;


		for(int y = y0; y < y1; y++)
		{

			int y_offset = y - y0;
			#pragma simd
			#pragma vector aligned
			for(int x = 0; x < width; x++)
			{
				float cur_ws_x = (x * x_inc) + base_x;

				int i = y_offset * width + x;

				info.ray_buffer->position.x[i] = cur_ws_x;
				info.ray_buffer->position.y[i] = cur_ws_y;
				info.ray_buffer->position.z[i] = cur_ws_z;
				
				float dx = cur_ws_x - eye_x;
				float dy = cur_ws_y - eye_y;
				float dz = cur_ws_z - eye_z;

				float mag_squared = dx * dx + dy + dy + dz + dz;
				float mag = sqrt(mag_squared);
				float invmag = 1/mag;


				info.ray_buffer->direction.x[i] = dx * invmag;
				info.ray_buffer->direction.y[i] = dy * invmag;
				info.ray_buffer->direction.z[i] = dz * invmag;

			}
			cur_ws_y += y_inc;
		}
	}
	void generate_eye_rays(const GenerateEyeRaysInfo& info)
	{
		int y0 = info.y0;
		int y1 = info.y1;
		int width = info.width;
		int blocks = ceil(width / 8.0);

		v3stream dp_per_pixel(info.dp_per_pixel.x, 
			info.dp_per_pixel.y, info.dp_per_pixel.z);

		v3stream frustrum_top_left(info.frustrum_top_left.x,
			info.frustrum_top_left.y, info.frustrum_top_left.z);

		v3stream eye_position(info.eye_position.x,
			info.eye_position.y, info.eye_position.z);

		int offset = info.buffer_offset;

		auto __restrict px_ptr = info.ray_buffer->position.x + offset;
		auto __restrict py_ptr = info.ray_buffer->position.y + offset;
		auto __restrict pz_ptr = info.ray_buffer->position.y + offset;

		auto __restrict dx_ptr = info.ray_buffer->direction.x + offset;
		auto __restrict dy_ptr = info.ray_buffer->direction.y + offset;
		auto __restrict dz_ptr = info.ray_buffer->direction.z + offset;
		for(int r = y0; r < y1; r++)
		{
			f32stream x_pixel_base = zero;
			f32stream y_pixel(r);
			for(int i = 0; i < blocks; i++)
			{
				f32stream x_pixel = x_pixel_base + increasing;
				x_pixel_base += eight;

				v3stream pixel_position(x_pixel, y_pixel, zero);

				auto world_position = pixel_position * dp_per_pixel
					+ frustrum_top_left;

				auto world_direction = world_position - eye_position;
				world_direction.normalize();

				*px_ptr = world_position.x;
				*py_ptr = world_position.y;
				*pz_ptr = world_position.z;

				*dx_ptr = world_direction.x;
				*dy_ptr = world_direction.y;
				*dz_ptr = world_direction.z;

				px_ptr++; py_ptr++; pz_ptr++;
				dx_ptr++; dy_ptr++; dz_ptr++;
			}
		}
	}
}


struct RenderThread : public QThread
{
	void run()
	{
		auto fb_size = target->buffer->size();
		int fb_width = fb_size.width();
		int fb_height = fb_size.height();
		vec3 eye_position(0, 0, 0);
		vec3 focus(0, 0, -1);
		float sphere_radius = 2;
		vec3 light_origin(0, 0, 0);

		QElapsedTimer timer;		

		timer.start();
		int frames = 0;
		//assumes screen can't be bigger than 3k
		__m256 increasing = {0, 1, 2, 3, 4, 5, 6, 7};
		


		while(true)
		{
			frames ++;
			target->resize_if_necessary();
			
			light_origin += vec3(.01, 0, 0);

			QRgb* fb_data = (QRgb*)target->buffer->bits();
			fb_size = target->buffer->size();
			fb_width = fb_size.width();
			fb_height = fb_size.height();

			
			float2 viewport = float2(fb_width, fb_height);

			auto view = view_matrix(eye_position, focus, vec3(0, 1, 0));
			auto proj = proj_matrix(fb_width, fb_height);

			auto viewproj = proj * view;

			auto inv_viewproj = inverse(viewproj);				
			
			sphere_radius += 0.004;

			vec3 sphere_origin(0, 0, -10);

			v3stream sphere_origin_v3s(as_mm256(sphere_origin.x), as_mm256(sphere_origin.y), as_mm256(sphere_origin.z));

			Frustrum frustrum = inv_viewproj_to_frustrum(inv_viewproj, viewport);

			VecSOA top_left;
			top_left.x = _mm256_broadcast_ss(&frustrum.top_left.start.x);
			top_left.y = _mm256_broadcast_ss(&frustrum.top_left.start.y);
			top_left.z = _mm256_broadcast_ss(&frustrum.top_left.start.z);

			vec3 w = screen_to_world(vec2(0, 0), 1, viewport, inv_viewproj);

			vec3 world_offsets = world_offsets_per_pixel(45, viewport, fb_width / (float)fb_height, 1);
			RaySphereIntersection rs_intersection;

			VecSOA eye_position_soa;
			eye_position_soa.x = as_mm256(eye_position.x);
			eye_position_soa.y = as_mm256(eye_position.y);
			eye_position_soa.z = as_mm256(eye_position.z);

			__m256 sphere_radius_256 = as_mm256(sphere_radius);
			RaySOA ray_buffer[800];
			align32 RayBuffer ray_buffer2;

			VecSOA world_offsets_soa;
			world_offsets_soa.x = _mm256_broadcast_ss(&world_offsets.x);
			world_offsets_soa.y = _mm256_broadcast_ss(&world_offsets.y);

			float fb_height_f = (float)fb_height;
			float fb_width_f = (float)fb_width;

			int total_rays = fb_width * fb_height;
			int current_ray = 0;
			

			__m256 data = SIMD_CONSTANTS(
				frustrum.top_left.start.x,
				frustrum.top_left.start.y,
				world_offsets.x,
				world_offsets.y
			);
			__m256 data2 = SIMD_CONSTANTS(
				frustrum.top_left.start.z,
				eye_position.x,
				eye_position.y,
				eye_position.z
			);
			int cur_x = 0, cur_y = 0; //to get around %, div in tight loop
			//batch
			while(cur_y < fb_height)
			{
				cur_x = 0;
				__m256 y_256 = as_mm256(cur_y);
				int total_blocks = (int)ceil(fb_width / 8.0);

				if(0)
				//generate rays
				{
					auto x_base_256 = as_mm256(cur_x);
					auto current_raysoa = ray_buffer;
					for(int i = 0; i < total_blocks; i++)
					{				
						auto x_256 = _mm256_add_ps(x_base_256, increasing);
						cur_x += 8;
						x_base_256 = as_mm256(cur_x);
						//WARNING: this is not correct. in view, but should be in world

						auto dx_avx = _mm256_mul_ps(SIMD_CONSTANT_2(data), x_256);
						auto rx = _mm256_add_ps(SIMD_CONSTANT_0(data), dx_avx);
						auto rz = SIMD_CONSTANT_0(data2);


						//do this last (pipeline stuff)
						auto dy_avx = _mm256_mul_ps(SIMD_CONSTANT_3(data), y_256);
						auto ry = _mm256_add_ps(SIMD_CONSTANT_1(data), dy_avx);
						current_raysoa->origin.x = rx;
						current_raysoa->origin.y = ry;
						current_raysoa->origin.z = rz;

						current_raysoa->direction.x = subtract(rx, SIMD_CONSTANT_1(data2));
						current_raysoa->direction.y = subtract(ry, SIMD_CONSTANT_2(data2));
						current_raysoa->direction.z = subtract(rz, SIMD_CONSTANT_3(data2));
						
						current_raysoa += 1;
						//store-load block... factored into another loop
						//current_raysoa->direction.normalize();
					}
					for(int i = 0; i < total_blocks; i++)
					{
						ray_buffer[i].direction.normalize();
					}
				}
				else
				{
					eyeray::GenerateEyeRaysInfo info;

					info.dp_per_pixel = world_offsets; 
					info.frustrum_top_left = frustrum.top_left.start;
					info.buffer_offset = 0;
					info.y0 = cur_y;
					info.y1 = cur_y + 1;
					info.width = fb_width;
					info.eye_position = eye_position;
					info.ray_buffer = &ray_buffer2;

					eyeray::generate_eye_rays(info);
				}
				//current_ray goes over total rays count
				int rays_in_batch = fb_width;

				align32 RaySphereIntersectionSOA rsi_soa[800];

				ray_sphere_intersection(sphere_origin_v3s, sphere_radius_256, ray_buffer, rsi_soa, total_blocks);
				/*
				for(int i = 0; i < total_blocks; i++)
				{
					ray_sphere_intersection(sphere_origin_v3s, sphere_radius_256, ray_buffer[i], rsi_soa[i]);
				}
				*/
				
				light(fb_data, rsi_soa, cur_y * fb_width, rays_in_batch);
				cur_y ++;
			}			
			qWarning("w=%d, h=%d, frame time = %f", fb_width, fb_height, timer.elapsed() / (float)frames);
		}
	}
	FrameBuffer* target;
};
int main(int argc, char *argv[])
{
	QApplication a(argc, argv);

	Editor w;
	FrameBuffer ew(&w);
	w.setCentralWidget(&ew);
	ew.winId();
	w.show();

	RenderThread rt;
	//rt.keyboard_state = &w.keyboard_state;
	//rt.mouse_state = &ew.mouse_state;
	rt.target = &ew;
	rt.start();

	return a.exec();
}
/*
struct VS_Bones_CB
{
	__declspec(align(16)) float4x4 bone_transforms[16];
	__declspec(align(16)) uint bone_count;
};
struct VS_PerObject_CB
{
	float4x4a world;
	float4x4a view;
	float4x4a worldview;
	float4x4a wvp;
};

struct Lighting_CB
{
	float4x4a g_inverse_projection;
	float4x4a g_inverse_view;
	float3a g_light_position_vs;
	float2a viewport_size;
};

XMMATRIX view_matrix(vec eye_pos, vec eye_focus, vec up)
{
	return XMMatrixLookAtLH(eye_pos, eye_focus, up);
}

XMMATRIX proj_matrix(float w, float h)
{
	const float Z_NEAR = 3000;
	const float Z_FAR = 1;
	return XMMatrixPerspectiveFovLH(XMConvertToRadians(75), w / h, Z_NEAR, Z_FAR );
}

void update_wvp_cb(shared_ptr<d3d::DXContext> context, d3d::Buffer* cb, Size size,
	vec eye_pos, vec eye_focus, vec up)
{
	auto ptr = context->lock_buffer<VS_PerObject_CB>(cb);

	auto world = XMMatrixIdentity();
	auto view = view_matrix(eye_pos, eye_focus, up);
	auto proj = proj_matrix(size.w, size.h);

	auto wvp = XMMatrixMultiply(XMMatrixMultiply(world, view), proj);
	auto worldview = XMMatrixMultiply(world, view);
	auto viewproj = XMMatrixMultiply(view, proj);
	
	
	XMStoreFloat4x4A(&ptr->wvp, XMMatrixTranspose(wvp));
	XMStoreFloat4x4A(&ptr->world, world);
	XMStoreFloat4x4A(&ptr->view, XMMatrixTranspose(view));
	XMStoreFloat4x4A(&ptr->worldview, XMMatrixTranspose(worldview));
	
	context->unlock_buffer(cb);
}
#include <fstream>
using namespace std;
struct RenderThread : public QThread
{
	EngineWidget* target;
	KeyboardState* keyboard_state;
	MouseState* mouse_state;
	void run()
	{
		uint width = target->width();
		uint height = target->height();

		shared_ptr<d3d::DXDevice> device;
		shared_ptr<d3d::DXContext> context;
		d3d::SwapChain* swap_chain;

		std::tie(device, context, swap_chain) = d3d::initialize(target->winId(), width, height);

		//auto geomdata = geom::animated_triangle();

		//auto geoms = asset::asset_to_geometries("meshes/Jane_hi_neutral.obj");
		if(0)
		{

			//auto geoms = asset::asset_to_modelgroup("meshes/Jane_hi_neutral.obj");
			auto geoms = asset::asset_to_modelgroup("meshes\\sponza.obj");
			auto os = ofstream("meshes\\scene.data", ios::binary);
			for(auto it = geoms.begin(); it != geoms.end(); it++)
			{
				it->material.albedo = L"meshes\\" + it->material.albedo;
				wstring target_path = it->material.albedo + L".dds";
				device->to_dds(it->material.albedo, target_path);
				it->material.albedo = target_path;


				it->material.normal = L"meshes\\" + it->material.normal;
				target_path = it->material.normal + L".dds";
				device->to_dds(it->material.normal, target_path);
				it->material.normal = target_path;
			}
			serialize_model_group(geoms, os);
			os.close();
		}
		
		auto is = ifstream("meshes\\scene.data", ios::binary);
		auto geoms = deserialize_model_group(is);
		is.close();
		//geom::write_to_file(geomdata, "./out.data");
		//auto geomdata = geom::load_from_file("./out.data");
		//auto geomdata = geom::triangle();
		//save_to_file(geoms[0], "./out.data");
		//auto geoms = std::vector<geom::Geometry*>();
		//auto geomdata = load_from_file<geom::Geometry>("./out.data");
		//geoms.push_back(load_from_file<geom::Geometry>("./out.data"));
		//geoms.push_back(geomdata);
		d3d::Shader make_gbuffer_static = device->create_shader_and_input_layout(L"shaders/make_gbuffer.hlsl", 
			eStatic, "vs_static");
		d3d::Shader make_gbuffer_animated = device->create_shader_and_input_layout(L"shaders/make_gbuffer.hlsl", 
			eAnimated, "vs_animated");
		d3d::Shader shade_using_gbuffer = device->create_shader_and_input_layout(L"shaders/shade_using_gbuffer.hlsl", 
			eStatic, "vs");


		auto fsquad_geom = fullscreen_quad();
		auto fsquad_vb = device->create_vb(fsquad_geom);
		auto fsquad_ib = device->create_ib(fsquad_geom);


		std::vector<d3d::VertexBuffer> vbs;
		std::vector<d3d::IndexBuffer> ibs;
		std::vector<d3d::ShaderResourceView*> srvs;
		std::vector<d3d::ShaderResourceView*> normal_srvs;
		for(auto it = geoms.begin(); it != geoms.end(); it++)
		{
			vbs.push_back(device->create_vb(it->geometry));
			ibs.push_back(device->create_ib(it->geometry));
			//GAMMA
			srvs.push_back(device->load_texture(it->material.albedo, DXGI_FORMAT_BC1_UNORM_SRGB));
			auto n = it->material.normal;
			normal_srvs.push_back(device->load_texture(it->material.normal, DXGI_FORMAT_BC1_UNORM));
		}


		rendering::gbuffer::GBuffer gbuffer = rendering::gbuffer::create(device, Size(width, height));

		auto swapchain_rtv = device->create_rtv(swap_chain);


		float4 color = float4(0, 0, 0, 1);

		auto make_gbuffer_vs_cb = device->create_cb<VS_PerObject_CB>();
		auto shade_using_gbuffer_lighting_cb = device->create_cb<Lighting_CB>();

		context->set_viewport(width, height);
		
		D3D11_DEPTH_STENCIL_DESC dsd;
		ZeroMemory(&dsd, sizeof(decltype(dsd)));
		dsd.DepthFunc = D3D11_COMPARISON_GREATER;
		dsd.DepthEnable = 1;
		dsd.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
		
		ID3D11DepthStencilState* dss;
		device->m_device->CreateDepthStencilState(&dsd, &dss);
		context->m_context->OMSetDepthStencilState(dss, 0);
		
		d3d::SamplerState* sampler_state = device->create_sampler_state(D3D11_FILTER_ANISOTROPIC );

		auto make_gbuffer_bones_cb = device->create_cb<VS_Bones_CB>();

		d3d::Buffer* cbs[2];
		cbs[0] = make_gbuffer_vs_cb;
		cbs[1] = make_gbuffer_bones_cb;

		rendering::light::PointLight pl;
		pl.transform.translation = to_vec(float3(200, 500, 0));
		pl.color = float3(1, 0, 0);

		Transform view;
		//view.translation = to_vec(float3(0, 0, 484));
		//view.lookat(to_vec(float3(0, 0, 483)), to_vec(float3(0, 1, 0)));
		view.translation = to_vec(float3(0, 0, -10));
		view.lookat(to_vec(float3(0, 0, 0)), to_vec(float3(0, 1, 0)));
		

		QPointF previous_position;

		int count = 0;

		while(true)
		{
			count+= 1;
			if(width != target->width() || height != target->height())
			{
				width = target->width();
				height = target->height();

				rendering::gbuffer::resize(device, &gbuffer, Size(width, height));
				device->resize(swap_chain, Size(width, height), &swapchain_rtv);


				context->set_viewport(width, height);
			}
			if(keyboard_state->is_pressed(Qt::Key_W))
			{
				view.translate(view.forward() * 2);
			}
			else if(keyboard_state->is_pressed(Qt::Key_S))
			{
				view.translate(-view.forward() * 2);
			}
			if(keyboard_state->is_pressed(Qt::Key_A))
			{
				view.translate(view.left() * 2);
			}
			else if(keyboard_state->is_pressed(Qt::Key_D))
			{
				view.translate(-view.left() * 2);
			}
			

			//view.rotate_ypr(view.translation(), dp.x() / 5,0, 0);
			
			auto dp = *mouse_state - previous_position;
			view.rotate_local(-dp.x()/700, -dp.y()/700, 0);//-dp.y() / 5, 0);
			previous_position = *mouse_state;
			
			{
				update_wvp_cb(context, make_gbuffer_vs_cb, Size(width, height),
					view.translation, view.translation + view.forward(), view.up());
				
				auto proj = proj_matrix(width, height);
				auto view_mat = view_matrix(view.translation, view.translation + view.forward(), view.up());

				XMVECTOR dummy;

				auto ptr2 = context->lock_buffer<Lighting_CB>(shade_using_gbuffer_lighting_cb);
				auto inv_proj = XMMatrixInverse(&dummy, proj);
				XMStoreFloat4x4(&ptr2->g_inverse_projection, XMMatrixTranspose(inv_proj));
				XMStoreFloat4x4(&ptr2->g_inverse_view, XMMatrixTranspose(XMMatrixInverse(&dummy, view_mat)));

				auto light_pos_in_vs = XMVector3TransformCoord(pl.transform.translation, view_mat);
				auto light_pos_in_ws = XMVector3TransformCoord(light_pos_in_vs, XMMatrixInverse(&dummy, view_mat));

				to_float3a(light_pos_in_vs, ptr2->g_light_position_vs);

				ptr2->viewport_size = float2a(width, height);
				context->unlock_buffer(shade_using_gbuffer_lighting_cb);


				auto ptr3 = context->lock_buffer<VS_Bones_CB>(make_gbuffer_bones_cb);
				ptr3->bone_count = 2;
				XMStoreFloat4x4(&ptr3->bone_transforms[0], XMMatrixTranslation(count, 0, 0));
				XMStoreFloat4x4(&ptr3->bone_transforms[1], XMMatrixTranslation(0, count, 0));
				context->unlock_buffer(make_gbuffer_bones_cb);
				
			}
			context->clear(swapchain_rtv, float4(0, 0, 0, 0));
			context->clear(gbuffer.albedo_rtv, float4(0, 0, 0, 0));
			context->clear(gbuffer.normal_rtv, float4(0, 0, 0, 0));
			context->clear(gbuffer.depth_dsv, 0);

			context->m_context->PSSetSamplers(0, 1, &sampler_state);


			context->m_context->OMSetRenderTargets(2, gbuffer.rtvs, gbuffer.depth_dsv);

			for(int i = 0; i < vbs.size(); i++)
			{
				d3d::ShaderResourceView *const srv[] = {srvs.at(i), normal_srvs.at(i), nullptr};		
				context->m_context->PSSetShaderResources(0, 3, srv);
				context->draw(vbs.at(i), ibs.at(i), make_gbuffer_static, 
					cbs, 2, cbs, 2);
			}
			context->m_context->OMSetRenderTargets(1, &swapchain_rtv, nullptr);
			context->m_context->PSSetShaderResources(0, 3, gbuffer.srvs);

			context->draw(fsquad_vb, fsquad_ib, shade_using_gbuffer,
				nullptr, 0, &shade_using_gbuffer_lighting_cb, 1);
				
			swap_chain->Present(0, 0);
			this->msleep(10);
		}
	}
};
int main(int argc, char *argv[])
{
	QApplication a(argc, argv);

	Editor w;
	EngineWidget ew(&w);
	w.setCentralWidget(&ew);
	ew.winId();
	w.show();
	
	RenderThread rt;
	rt.keyboard_state = &w.keyboard_state;
	rt.mouse_state = &ew.mouse_state;
	rt.target = &ew;
	rt.start();

	return a.exec();
}
*/